'''
CSPBuilder plugin.

Provides access to the CSPShare server

Author: Esben Bugge, 2009.
'''

PLUGIN_NAME = 'CSPShare'

# server settings
#CSPSHARE_URL = 'http://cspshare.appspot.com'
CSPSHARE_URL = 'http://localhost:9999'
CLIENTLOGIN_URL = 'https://www.google.com/accounts/ClientLogin'
APP_NAME = 'CSPShare-1.0'

try:
    import cPickle as pickle
except:
    import pickle

import os
import wx
from urllib import urlencode
from google.appengine.api import urlfetch, urlfetch_stub
from google.appengine.api import apiproxy_stub_map

from plugins.builder import *
import xml.dom.minidom
from extensions import cspxml
from builder import *
import os.path
import zlib
import re
import time

global user, passwd, userChanged, cookie
user = ''
passwd = ''
userChanged = False
cookie = ''

class PluginCSPShare(PluginBUILDER):
    '''
    Class to represent the CSPShare plugin
    '''
    
    def __init__(self, pluginManager):
        PluginBUILDER.__init__(self, pluginManager, PLUGIN_NAME)
        
        # init proxy for urlfetch
        apiproxy_stub_map.apiproxy = apiproxy_stub_map.APIProxyStubMap()
        apiproxy_stub_map.apiproxy.RegisterStub('urlfetch',urlfetch_stub.URLFetchServiceStub())
        
        self.home = os.getenv('USERPROFILE') or os.getenv('HOME')
        self.confPathAndFilename = self.home + '/.cspbuilder/builder.conf'
        
        self._load_pref()
        
        #global passwd 
        #self.passwd = ''
        #self.userChanged = False # whether a new username/passwd has been entered
        
        #self.cookieHdr = {}
        
        # whether the server list has been refreshed
        self.refreshed = False
        
        
    def buildCompDOM(self, name='',package='',comment='',users=[],cspContent='',pyContent='',key=''):
        '''
        Create a XML doc for a single component.
        All parameters are optional so we can upload parts of a component.
        '''
        doc = xml.dom.minidom.Document()
        root = doc.createElement('doc')
        doc.appendChild(root)
        compElem = doc.createElement("component")
        
        # key
        if key:
            elem = doc.createElement("key")
            txtElem = doc.createTextNode(key)
            elem.appendChild(txtElem)
            compElem.appendChild(elem)
        
        # name
        if name:
            elem = doc.createElement("name")
            txtElem = doc.createTextNode(name)
            elem.appendChild(txtElem)
            compElem.appendChild(elem)
        
        # package
        if package:
            elem = doc.createElement("package")
            txtElem = doc.createTextNode(package)
            elem.appendChild(txtElem)
            compElem.appendChild(elem)
            
        # comment
        if comment:
            elem = doc.createElement("comment")
            #elem.setAttribute('version',str(version))
            txtElem = doc.createTextNode(comment)
            elem.appendChild(txtElem)
            compElem.appendChild(elem)
        
           
        if len(users):
            usersElem = doc.createElement("users")
            compElem.appendChild(usersElem)
            for user in users:
                itemElem = doc.createElement("user")
                item = doc.createTextNode(user)
                itemElem.appendChild(item)
                usersElem.appendChild(itemElem)
            
        # content from csp files
        if cspContent:
            elem = doc.createElement("csp")
            #elem.setAttribute('version',str(version))
            txtElem = doc.createTextNode(cspContent)
            elem.appendChild(txtElem)
            compElem.appendChild(elem)
            
        # content from csp files
        if pyContent:
            elem = doc.createElement("python")
            #elem.setAttribute('version',str(version))
            txtElem = doc.createTextNode(pyContent)
            elem.appendChild(txtElem)
            compElem.appendChild(elem)
                
        # latest version (required)
        #if version:
        #    elem = doc.createElement("latestVer")
        #    txtElem = doc.createTextNode(str(version))
        #    elem.appendChild(txtElem)
        #    compElem.appendChild(elem)
        
        root.appendChild(compElem)
        return doc
        
    # ----------------------------------------- #
    # --- Packing and un-packing components --- #
    # ----------------------------------------- #
    
    def _packContent(self, compPath, version):
        '''
        Pack csp-file and python-file content. Returns a
        tuple with three strings: cspContent (packed), pyContent (packed),
        plus an XML DOM instance that represents the csp content that was
        packed.
        Packed means compressed and encoded in base64.
        @param compPath:
        @param version: The version of the content to be packed
        @param newVersion: The version that the component will get on server
        '''
        cspContent = ''
        pyContent = ''
        verDir = 'e_version%06d' % version
        
        # csp
        cspPath = os.path.join(compPath, verDir, 'main.csp')
        f = open(cspPath)
        fileContent = f.read()
        f.close()
        #document = cspxml.parseFile(cspPath)
        #for e in document.getElementsByTagName('process'):
        #    if e.parentNode.nodeName == 'process':
        #        e.setAttribute('version',str(newVersion))
        # 
        #newContent = document.toxml()
        cspContent = zlib.compress(fileContent).encode('base64')
        
        # python code
        pyPath = os.path.join(compPath, verDir, 'main.py')
        if os.path.exists(pyPath):
            f = open(pyPath)
            fileContent = f.read()
            f.close()
            pyContent = zlib.compress(fileContent).encode('base64')
        
        return cspContent, pyContent
    
#    def _packContent_(self, compPath):
#        
#        cspContent = []
#        
#        data = []
#        versionDirs = os.listdir(compPath)
#        versions = []
#        cspFiles = []
#        pyFiles = []
#        for v in versionDirs:
#            if v.startswith('version'):
#                versions.append(str(int(v[-6:])))
#                cspPath = os.path.join(compPath, v, 'main.csp')
#                if os.path.exists(cspPath):
#                    cspFiles.append('---next:csp---')
#                    f = open(cspPath)
#                    fileContent = f.read()
#                    f.close()
#                    cspFiles.append(fileContent)
#                    cspContent.append(fileContent.encode('base64'))
#                
#                pyPath = os.path.join(compPath, v, 'main.py')
#                if os.path.exists(pyPath):
#                    pyFiles.append('---next:py---')
#                    f = open(pyPath)
#                    fileContent = f.read()
#                    f.close()
#                    pyFiles.append(fileContent)
#                    
#        #versionStr = ','.join(versions)
#        cspStr = '\n'.join(cspFiles)
#        pyStr = '\n'.join(pyFiles)
#        data.append(cspStr)
#        data.append(pyStr)
#        content = '\n'.join(data)
#        content = zlib.compress(content).encode('base64')
#        
#        return content, cspContent
        
    def _parseCompDoc(self, doc):
        compdata = {}
        for compElem in doc.getElementsByTagName('component'):
                
            #print compElem.childNodes
            
            # package
            e = compElem.getElementsByTagName('package').item(0)
            package = e.firstChild.data
            if not compdata.has_key(package):
                compdata[package] = {}
                
            # component name
            e = compElem.getElementsByTagName('name').item(0)
            name = e.firstChild.data
            compdata[package][name] = {}
            
            compdata[package][name]['csp'] = {}
            compdata[package][name]['py'] = {}
            compdata[package][name]['comment'] = {}
            compdata[package][name]['users'] = []
            
            
            #for e in compElem.childNodes:
                
            # key
            for e in compElem.getElementsByTagName('key'):
                key = e.firstChild.data
                compdata[package][name]['key'] = key
                        
            # latest version
            #if e.tagName == 'latestVersion':
            for e in compElem.getElementsByTagName('latestVersion'):
                latestVersion = e.firstChild.data
                compdata[package][name]['latestVersion'] = latestVersion
                        
            # comment
            #if e.tagName.startswith('comment'):
            for e in compElem.getElementsByTagName('comment'):
                version = e.getAttribute('version')
                content = zlib.decompress(e.firstChild.data.decode('base64'))
                compdata[package][name]['comment'][version] = content
                    
            # csp content
            #if e.tagName.startswith('csp'):
            for e in compElem.getElementsByTagName('csp'):
                version = e.getAttribute('version')
                content = zlib.decompress(e.firstChild.data.decode('base64'))
                compdata[package][name]['csp'][version] = content
                    
            # python content
            #if e.tagName.startswith('python'):
            for e in compElem.getElementsByTagName('python'):
                version = e.getAttribute('version')
                content = zlib.decompress(e.firstChild.data.decode('base64'))
                compdata[package][name]['py'][version] = content
                    
            # collaborators
            #if e.tagName == 'users':
            for e in compElem.getElementsByTagName('users'):
                for u in e.getElementsByTagName('user'):
                    collaborator = u.firstChild.data
                    compdata[package][name]['users'].append(collaborator)
                        
        return compdata
    
    def _unpackContent(self, dom):
        if dom.firstChild.nodeName == 'list':
            similarNames = 0
            listElem = dom.firstChild
            for compElem in listElem.getElementsByTagName('component'):
                        
                # package
                e = compElem.getElementsByTagName('package').item(0)
                package = e.firstChild.data
                if not self.serverData.has_key(package):
                    self.serverData[package] = {}
                        
                # name
                e = compElem.getElementsByTagName('name').item(0)
                name = e.firstChild.data
                listName = name # listName is the name of the component as it appears in the list
                if self.serverData[package].has_key(name):
                    similarNames = similarNames+1
                    listName = name + ' (' + str(similarNames) + ')'
                self.serverData[package][listName] = {'name': name}
                            
                self.serverData[package][listName]['csp'] = {}
                self.serverData[package][listName]['py'] = {}
                self.serverData[package][listName]['comment'] = {}
                self.serverData[package][listName]['users'] = []
                for e in compElem.childNodes:
                    
                    # key
                    if e.tagName == 'key':
                        key = e.firstChild.data
                        self.serverData[package][listName]['key'] = key
                        
                    # latest version
                    if e.tagName == 'latestVersion':
                        latestVersion = e.firstChild.data
                        self.serverData[package][listName]['latestVersion'] = latestVersion
                        
                    # comment
                    if e.tagName.startswith('comment'):
                        version = e.tagName.lstrip('comment')
                        content = zlib.decompress(e.firstChild.data.decode('base64'))
                        self.serverData[package][listName]['comment'][version] = content
                    
                    # csp content
                    if e.tagName.startswith('csp'):
                        version = e.tagName.lstrip('csp')
                        content = zlib.decompress(e.firstChild.data.decode('base64'))
                        self.serverData[package][listName]['csp'][version] = content
                    
                    # python content
                    if e.tagName.startswith('python'):
                        version = str(e.tagName).lstrip('python')
                        content = zlib.decompress(e.firstChild.data.decode('base64'))
                        self.serverData[package][listName]['py'][version] = content
                    
                    # collaborators
                    if e.tagName == 'users':
                        for u in e.childNodes:
                            collaborator = u.firstChild.data
                            self.serverData[package][listName]['users'].append(collaborator)
        
    def _startup(self,frame):
        '''
        Function to be run at startup. Displays a message that the user
        should input username and password to CSPShare server.
        @param frame:
        '''
        
        global user, passwd
        cookieHdr, expired = self._getCookieHdr()
        if (not cookieHdr or expired):
            dlg = wx.MessageDialog(None, 'To get access to the CSPShare server, where components can be shared you will need to enter your Google-account information. Upon closing this dialog you will be presented with the option to add this information. Your e-mail address will be saved to disk - the password is only used for this session.',
                                   'CSPShare access',wx.OK)
            dlg.ShowModal()
            dlg.Destroy()
        
            self._prefmenu_activate(frame, None)

            
    def _prefmenu_activate(self, frame, event):
        global user, passwd, userChanged
        dialog = UserPassDialog(frame,info='Enter Google-account information for access to CSPShare.')

        if dialog.ShowModal() == wx.ID_OK:
            userChanged = True
            user = dialog.GetEmail()
            passwd = dialog.GetPassword()
            self._save_pref()
        dialog.Destroy()
    
    def _prefmenu(self, frame, menu, menuid):
        menu.Append(menuid, 'Configure access to CSPShare')
        frame.Bind(wx.EVT_MENU, lambda event:self._prefmenu_activate(frame,event), id=menuid)
        
    def __load_all_pref(self):
        D = {}
        if os.path.exists(self.confPathAndFilename):
            # Load
            f = open(self.confPathAndFilename, 'r')
            filedata = ''
            for line in f.readlines():
                filedata += line
            D = pickle.loads(filedata)
            f.close()
        else:
            self._create_empty_pref()

        return D
    
    def _save_pref(self):
        global user, cookie, userChanged
        D = self.__load_all_pref()

        D['email'] = user
        D['cookie'] = cookie
        # we need to store this if the user changes the username/passwd and
        # exits wihtout authenticating
        #print 'saving user changed:',str(userChanged)
        D['userChanged'] = str(userChanged)

        f = open(self.confPathAndFilename, "wb")
        f.write(pickle.dumps(D))
        f.close()
    
    def _load_pref(self):
        global user, cookie, userChanged
        D = self.__load_all_pref()

        if D.has_key('email'):
            user = D['email']
        else:
            user = ''
            
        if D.has_key('cookie'):
            cookie = D['cookie']
        else:
            cookie = ''
            
        if D.has_key('userChanged'):
            userChanged = D['userChanged'] == 'True'
        else:
            userChanged = False
        #print 'Got userChanged:',userChanged
            
    def _rcmenu(self, frame, menu, obj, menuid):
        menu.Append(menuid, 'Select Version')
        frame.Bind(wx.EVT_MENU, lambda event:self._rcmenu_activate(frame,event), id=menuid)

    def _rcmenu_activate(self,frame,event):
        
        # find available versions and put them in a list
        obj = frame.selection[0]
        versions = obj.getAvailVersions()
        choices = ['newest']
        for v in versions['editVersions']:
            choices.append('e'+ str(v))
        for v in versions['serverVersions']:
            choices.append(str(v))
            
        # show dialog with correct selection 
        selectedIdx = choices.index(obj.getVersion().lower())
        dlg = wx.SingleChoiceDialog(frame,'Select which version of this component that will be used:','Select Version',choices=choices)
        dlg.SetSelection(selectedIdx)
        if dlg.ShowModal() == wx.ID_OK:
            
            # set version from user-selection
            selectedVersion = dlg.GetStringSelection().lower()
            obj.setVersion(selectedVersion)
            frame.dirty = True
        dlg.Destroy()
        
    def _getServerComps(self, parentFrame):
        
        self.serverData = {}
            
        #target = CSPSHARE_URL + '/rest/component'
        target = CSPSHARE_URL + '/component'
        cookieHdr = self._authenticate(target)
        if cookieHdr:
            try:
                result = urlfetch.fetch(target, headers=cookieHdr, follow_redirects=False)
                if result.status_code == 200:
                    #print 'content:',result.content
                    doc = xml.dom.minidom.parseString(result.content)
                    self.serverData = self._parseCompDoc(doc)
                    #self._unpackContent(doc)
                    if not self.serverData:
                        dlg = wx.MessageDialog(parentFrame, "No components on server.",'Download failed',
                                           wx.OK|wx.ICON_EXCLAMATION)
                        dlg.ShowModal()
                        dlg.Destroy()
                        return False
                    else:
                        return True
                else:
                    print result.status_code
                    print result.content
                    dlg = wx.MessageDialog(parentFrame, "Couldn't get components from server!",'Download failed',
                                           wx.OK|wx.ICON_EXCLAMATION)
                    dlg.ShowModal()
                    dlg.Destroy()
                    return False
            except urlfetch.DownloadError:
                dlg = wx.MessageDialog(frame, 'Unable to reach server. Try again.','Timeout',wx.OK)
                dlg.ShowModal()
                dlg.Destroy()
                return False
                

    def _share_activate(self, frame, event):
        global user
    
        # the user needs to save contents before she can share them
        if frame.dirty or not frame.compPath:
            dlg = wx.MessageDialog(frame, 'Please save the contents of CSPBuilder before sharing components.','Save needed',wx.OK)
            dlg.ShowModal()
            dlg.Destroy()
            return
        
        # we don't need to share versions that are already on server
        if not frame.editVersion:
            dlg = wx.MessageDialog(frame, 'The version of this component is already on the server.','Sharing not needed',wx.OK)
            dlg.ShowModal()
            dlg.Destroy()
            return
        
        # we can't share components that refer to local-only components
        for obj in frame.contents:
            if obj.obj_type == obj_PROCESS and obj.isLink() and obj.getVersion().startswith('e'):
                dlg = wx.MessageDialog(frame, obj.name + ' within this component is an edit-version. It is not allowed to share components that contain edit-version components.','Contains edit-version',wx.OK)
                dlg.ShowModal()
                dlg.Destroy()
                return
            
        # authenticate: we cannot share anything without authentication
        #target = CSPSHARE_URL + '/rest/component'
        target = CSPSHARE_URL + '/component'
        cookieHdr = self._authenticate(target)
        print 'cookieHdr:', cookieHdr
        if not cookieHdr:
            return # no cookie == not authenticated
            
        # get info on component existing on server if we have a key
        compKey = frame.key
        #nextVersion = 1
        collaborators = []
        package = frame.package_name
        compName = frame.compName

        if compKey:
            #target = CSPSHARE_URL + '/rest/component/' + compKey
            try:
                headers = cookieHdr.copy()
                headers['Key'] = compKey
                # get component on server with given key
                print 'compKey', compKey
                result = urlfetch.fetch(target, method=urlfetch.GET, headers=headers)
                if result.status_code == 200:
                    print 'result:', result.content
                    doc = cspxml.parseString(result.content)
                    
                    # latest version, used to determine next version 
                    #for elem in doc.getElementsByTagName('latestVer'):
                    #    latest = int(elem.firstChild.data)
                    #    nextVersion = latest+1
                    
                    for elem in doc.getElementsByTagName('users'):
                        for item in elem.getElementsByTagName('user'):
                            collaborators.append(item.firstChild.data)
                            
                elif result.status_code == 401:
                    dlg = wx.MessageDialog(frame, 'You do not have permission to share this component. You can save it under another name and try again.','No permission',wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                    return
                elif result.status_code == 500:
                    dlg = wx.MessageDialog(frame, 'An error occured on the server.','Server error',wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                    return
            except urlfetch.DownloadError:
                dlg = wx.MessageDialog(frame, 'Unable to reach server. Try again.','Timeout',wx.OK)
                dlg.ShowModal()
                dlg.Destroy()
                return
        
        # check permission if collaborators have been found
        #print 'Found collaborators:',collaborators
        #if collaborators and not collaborators.count(user):
            
        # show the Share-dialog
        shareDlg = ShareDialog(frame,compName,package,collaborators)
        
        if shareDlg.ShowModal() == wx.ID_OK:
            
            print 'target:',target
            
            # get name and package plus comments and collaborators from dialog
            comment = zlib.compress(shareDlg.GetComment()).encode('base64')
            collaborators = shareDlg.GetCollaborators()
            if not collaborators.count(user):
                collaborators.append(user)
            
            
            # pack csp and python content
            #cspContent, pyContent, newCspDoc = self._packContent(frame.compPath, frame.compVersion, nextVersion)
            cspContent, pyContent = self._packContent(frame.compPath, frame.compVersion)
            
            # prepare payload
            if compKey:
                # no need to upload this again
                compName = '' 
                package = ''
            doc = self.buildCompDOM(compName, package, comment, collaborators, cspContent, pyContent, compKey)
            print 'to share:',doc.toprettyxml('  ')
            
            try:
                result = urlfetch.fetch(target, doc.toxml(), urlfetch.POST, cookieHdr)
                if result.status_code == 200:                    
                    dlg = wx.MessageDialog(frame, frame.compName + ' was successfully shared.','Shared!',wx.OK)
                    
                    # from response: get key and version no.
                    print result.content 
                    doc = xml.dom.minidom.parseString(result.content)
                    for e in doc.getElementsByTagName('key'):
                        newkey = e.firstChild.data
                    for e in doc.getElementsByTagName('version'):
                        newversion = int(e.firstChild.data)                    
                        
                    oldDir = os.path.join(frame.compPath,'e_version%06d' % frame.compVersion)
                    
                    # set new kay and new version for local processes within component
                    oldCspPath = os.path.join(oldDir, 'main.csp')
                    cspDoc = xml.dom.minidom.parse(oldCspPath)
                    for doc in cspDoc.getElementsByTagName('doc'):
                        for e in doc.childNodes:
                            if e.nodeName == 'process':
                                # set key
                                if not frame.key: # we only need to set key if existing component didn't have key
                                    e.setAttribute('key',newkey)
                                
                                # set version on local process
                                for p in e.getElementsByTagName('process'):
                                    p.setAttribute('version',str(newversion))
                    
                    # rename dir with uploaded data so it
                    newDir = os.path.join(frame.compPath,'version%06d' % newversion)
                    os.rename(oldDir, newDir)
                    
                    # save contents
                    cspPath = os.path.join(newDir, 'main.csp')
                    f = open(cspPath, 'wb')
                    f.write(cspDoc.toxml())
                    f.close()
                    
                    # update local object(s)
                    for obj in frame.contents:
                        if obj.getObjType() == obj_PROCESS and not obj.isComposed():
                            
                            obj.setVersion(str(newversion))
                            obj.cspData['version'] = str(newversion)
                            
                            doc = xml.dom.minidom.parseString(obj.cspData['xml'])
                            for e in doc.getElementsByTagName('process'):
                                e.setAttribute('version',str(newversion))
                            obj.cspData['xml'] = doc.toxml()
                        
                    frame.key = newkey
                    frame.editVersion = False
                    frame.compVersion = newversion
                    frame.updateTitleVersion(str(newversion))
                    
                else:
                    print result.content
                    print result.headers
                    dlg = wx.MessageDialog(frame, 'Sharing was in-complete!','Share not completed!',wx.OK)
                dlg.ShowModal()
                dlg.Destroy()
            except urlfetch.DownloadError:
                errdlg = wx.MessageDialog(frame, 'Unable to reach server. Try again.','Timeout',wx.OK)
                errdlg.ShowModal()
                errdlg.Destroy()
                return
                        
        shareDlg.Destroy()
        
    def _draw_share_button(self, frame):

        panel = wx.Panel(frame.leftPanel, -1)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        panel.SetAutoLayout(True)
        panel.SetSizer(sizer)
        
        leftSizer = frame.leftPanel.GetSizer()
        leftSizer.Add(panel, flag = wx.ALIGN_CENTER)
        shareButton = wx.Button(panel, -1, "Share")
        sizer.Add(shareButton, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        panel.Bind(wx.EVT_BUTTON, lambda event:self._share_activate(frame,event), shareButton)
        
    def _setCookie(self, newCookie):
        global cookie
        cookie = newCookie
        
        # will also save user's e-mail, so when the user-pass dialog is opened,
        # the correct user should be in the e-mail field
        self._save_pref()
        
    def _getCookieHdr(self, localhost=False):
        if not localhost:
            global cookie
            expired = True
            cookieHdr = {}
            if cookie:
                lines = cookie.split(';')
                for l in lines:
                    line = l.split('=')
                    if line[0].strip() == 'expires':
                        expiration = time.strptime(line[1].strip(), '%a, %d-%b-%Y %H:%M:%S %Z')
                        print 'expiration:',time.strftime('%a, %d-%b-%Y %H:%M:%S %Z',expiration)
                        print 'current:', time.strftime('%a, %d-%b-%Y %H:%M:%S %Z',time.gmtime())
                        expired = expiration < time.gmtime()
                    elif line[0].strip() != 'path' and line[0].strip() != 'domain':
                        cookieHdr = {'Cookie': l } 
            return cookieHdr, expired
        else:
            cookieHdr = {'Cookie': 'dev_appserver_login="'+user+':True"; Path=/;'}
            return cookieHdr, False

    def _authenticate(self, target):
        
        # for testing
        if target.startswith('http://localhost'):
            cookieHdr, expired = self._getCookieHdr(True)
            return cookieHdr
        
        global user, passwd, userChanged
        cookieHdr, expired = self._getCookieHdr()
        
        if cookieHdr == {} or expired or userChanged:
            print 'We need a new cookie'
            cookieHdr = {}
            userChanged = False
            
            if not user or not passwd:
                userDlg = UserPassDialog(None,info='Please enter Google-account information before accessing CSPShare.')
                if userDlg.ShowModal() == wx.ID_OK and userDlg.GetEmail() != '' and userDlg.GetPassword()!= '':
                    #userChanged = True
                    user = userDlg.GetEmail()
                    passwd = userDlg.GetPassword()
                    self._save_pref()
                    userDlg.Destroy()
                else:
                    self._setCookie('')
                    dlg = wx.MessageDialog(None, "You didn't provide your account information, so you cannot get access to CSPShare.",'Authentication failed',wx.OK)
                    dlg.ShowModal()
                    dlg.Destroy()
                    userDlg.Destroy()
                    return cookieHdr
            
            # Get auth. token
            authreq_data = urlencode({ "Email": user, "Passwd":  passwd, "service": "ah", "source": APP_NAME, "accountType": "HOSTED_OR_GOOGLE" })
            auth_result = urlfetch.fetch(CLIENTLOGIN_URL,authreq_data,method=urlfetch.POST)
            auth_dict = dict([ x.split('=') for x in auth_result.content.split("\n") if (x) ])
            if auth_dict.has_key('Auth'):
                auth_token = auth_dict["Auth"]
                                
                # Get auth.cookie
                serv_args = {'auth': auth_token, 'continue': target}
                serv_uri = "%s/_ah/login?%s" % (CSPSHARE_URL, urlencode(serv_args))
                cookie_result = urlfetch.fetch(serv_uri,follow_redirects=False,method=urlfetch.GET)
                auth_cookie = cookie_result.headers['set-cookie'].split('=')
                
                self._setCookie(cookie_result.headers['set-cookie'])
                cookieHdr, expired = self._getCookieHdr()
                
                #auth_cookie[1] = auth_cookie[1].strip('; expires')
                #cookie = {'name':auth_cookie[0], 'value':auth_cookie[1]}
                #myHdr = {'Cookie' : cookie['name']+'='+cookie['value']}
                #print 'myHdr:',myHdr
                
            else:
                #not authorized!
                # remove cookie
                self._setCookie('')
                dlg = wx.MessageDialog(None, "Authentication failed. Please try and update your Google account information in File->Preferences->Configure access to CSPShare.",
                                       'Authentication failed',wx.OK|wx.ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
        
        else:
            print 'We already have a cookie!'
            
        return cookieHdr
            
    ##------------------------------------##
    ##----- Insert component-methods -----##
    ##------------------------------------##
    
    def _drawServerArea(self, sizer, panel):

        self.insertCompPanel = panel
        row1 = wx.BoxSizer(wx.HORIZONTAL)
        row2 = wx.BoxSizer(wx.HORIZONTAL)
        row3 = wx.BoxSizer(wx.HORIZONTAL)
        row4 = wx.BoxSizer(wx.HORIZONTAL)
        row5 = wx.BoxSizer(wx.HORIZONTAL)
        row6 = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(row1)
        sizer.Add(row2)
        sizer.Add(row3)
        sizer.Add(row4)
        sizer.Add(row5)
        sizer.Add(row6)
        
        # 1st row
        row1.Add(wx.StaticText(self.insertCompPanel, -1, "Download a component from CSPShare."),flag=wx.ALL,border=10)
        
        # 2nd row
        row2.Add(wx.StaticText(self.insertCompPanel, -1, "Package:"),flag=wx.LEFT|wx.TOP|wx.BOTTOM,border=10)
        self.servercb = wx.ComboBox(self.insertCompPanel, -1, '', choices=[], style=wx.CB_READONLY)
        row2.Add(self.servercb, flag=wx.ALL, border=10)
        self.insertCompPanel.Bind(wx.EVT_COMBOBOX, self._EvtServerPackBox, self.servercb)
        
        # 3rd row
        self.serverlb = wx.ListBox(self.insertCompPanel, -1, size=(250,300),choices=["Click 'Refresh' to get list","of components on server"], style=wx.LB_SINGLE)
        row3.Add(self.serverlb,flag=wx.ALL,border=10)
        self.insertCompPanel.Bind(wx.EVT_LISTBOX, self._EvtServerListBox, self.serverlb)
        self.insertCompPanel.Bind(wx.EVT_LISTBOX_DCLICK, self._EvtDwnlButton, self.serverlb) # double-click = download
        self.btnDwnl = wx.Button(self.insertCompPanel, wx.ID_ADD, "-->")
        self.btnDwnl.Disable()
        row3.Add(self.btnDwnl,flag=wx.ALL|wx.ALIGN_CENTER,border=10)
        self.insertCompPanel.Bind(wx.EVT_BUTTON, self._EvtDwnlButton, self.btnDwnl)
        
        # 4th row
        refreshBtn = wx.Button(self.insertCompPanel, wx.ID_REFRESH, 'Refresh')
        row4.Add(refreshBtn, flag=wx.ALL|wx.ALIGN_LEFT,border=10)
        self.insertCompPanel.Bind(wx.EVT_BUTTON, self._EvtRefreshButton, refreshBtn)
        
        # 5th row: info header
        row5.Add(wx.StaticText(self.insertCompPanel, -1, "Info on server component:"),flag=wx.ALL,border=10)
        
        # 6th row: info text 
        self.compInfo = wx.StaticText(self.insertCompPanel, -1, '',size=(300,100))
        row6.Add(self.compInfo,flag=wx.ALL,border=10)
        
    def _EvtRefreshButton(self, event):
        
        # get data from server
        if self._getServerComps(None):
            #print 'bla:', self.serverData
            packages = []
            for package in self.serverData.keys():
                packages.append(package)
            packages.sort()
            self.selectedPackage = packages[0]
            components = self.serverData[self.selectedPackage].keys()
            
            # update controls
            self.servercb.SetItems(packages)
            self.servercb.SetSelection(0)
            self.serverlb.SetItems(components)
            self.btnDwnl.Enable()
            self.refreshed = True
        
    def _EvtDwnlButton(self, evt):
        if self.selectedComponent != '':
            #dlg = wx.MessageDialog(None, 'Do you want to download ' + self.selectedComponent + '?',
            #                       caption='Download', style=wx.YES_NO)
            #if dlg.ShowModal() == wx.ID_YES:
                targets = self.manager.getList(pathlist_COMPONENTS) + [sys.path[0] + "/Components"]
                tarDlg = wx.SingleChoiceDialog(self.insertCompPanel,'Where should we put ' + self.selectedComponent + '?','Destination',choices=targets)
                if tarDlg.ShowModal() == wx.ID_OK:
                    dest = tarDlg.GetStringSelection()
                    component = self.serverData[self.selectedPackage][self.selectedComponent]
                    
                    # make new folder for component
                    compPath = os.path.join(dest,self.selectedComponent + '___csp')
                    if os.path.exists(compPath):
                        errDlg = wx.MessageDialog(None, 'Path already exists. No over-write functionality.', style=wx.OK)
                        errDlg.ShowModal()
                        errDlg.Destroy()
                        tarDlg.Destroy()
                        #dlg.Destroy()
                        return
                    os.mkdir(compPath)
                    
                    # save main.csp file
                    ver = 1
                    while component['csp'].has_key(str(ver)):
                        
                        # write key-attribute as it may not be present
                        document = cspxml.parseString(component['csp'][str(ver)])
                        for e in document.childNodes:
                            if (e.nodeName == 'doc'):
                                doc = e
                                for e1 in doc.childNodes:
                                    if e1.nodeName == 'process':
                                        e1.setAttribute('key',component['key'])
                                        
                                        # set version for 'process' tags as well as we can't upload with newVersion
                                        for e2 in e1.getElementsByTagName('process'):
                                            e2.setAttribute('version',str(ver))
                        
                        verDir = 'version%06d' % ver
                        path = os.path.join(compPath,verDir)
                        if not os.path.exists(path):
                            os.mkdir(path)
                        cspPath = os.path.join(compPath,verDir,'main.csp')
                        f = open(cspPath,'wb')
                        f.write(document.toxml('utf-8'))
                        f.close()
                        ver = ver + 1
                    
                    # save main.py file
                    ver = 1
                    while component['py'].has_key(str(ver)):
                        verDir = 'version%06d' % ver
                        pyPath = os.path.join(compPath,verDir,'main.py')
                        f = open(pyPath,'wb')
                        f.write(component['py'][str(ver)])
                        f.close()
                        ver = ver + 1
                    
                    self.insertCompPanel.AddComponent(self.selectedPackage, self.selectedComponent, component, compPath)
                    
                tarDlg.Destroy()
            #dlg.Destroy()
    
    def _EvtServerPackBox(self, evt):
        self.selectedPackage = evt.GetString()
        components = self.serverData[self.selectedPackage].keys()
        components.sort()
        self.serverlb.Set(components)
        
    def _EvtServerListBox(self, evt):
        if self.refreshed:
            self.selectedComponent = evt.GetString()
            if self.selectedComponent:
                component = self.serverData[self.selectedPackage][self.selectedComponent]
                
                # versions
                versions = ''
                for k in component['csp'].keys():
                    versions = versions + k + ', '
                
                # collaborators
                collaborators = ''
                for u in component['users']:
                    collaborators = collaborators + u.strip('\n') + ', '
                
                # permission
                if component['users'].count(user):
                    permission = 'Collaborator'
                else:
                    permission = 'Read-only'
                
                self.compInfo.SetLabel('Component: ' + self.selectedComponent + '\nVersions: ' + versions + '\nCollaborators: ' +  collaborators + '\nYour permissions: ' + permission)
        
def load(pluginManager):
    '''
    Load the CSPShare plugin
    @param pluginManager: The plugin manager to add the plugin to
    '''
    p = PluginCSPShare(pluginManager)
    p.addDrawInsertCompArea(location_INSERTCOMP_LEFT, p._drawServerArea)
    p.addRightClickElement(location_PROCESS_LINK, p._rcmenu)
    p.addLeftBarElement(p._draw_share_button)
    p.addToExecuteOnStartup(p._startup)
    p.addPrefMenuElement(p._prefmenu)
    pluginManager.addPlugin(p)
    
##--------------------------------------##
##--------------- Dialogs --------------##
##--------------------------------------##
    
class ShareDialog(wx.Dialog):
    
    def __init__(self, frame, compName="", package="", collaborators=[]):
        wx.Dialog.__init__(self,frame, -1, "Share " + compName + " on CSPShare", style = wx.DEFAULT_FRAME_STYLE | wx.WANTS_CHARS )
        win = wx.Panel(self, -1)
        
        # sizers
        panelSizer = wx.BoxSizer(wx.VERTICAL) # main sizer
        infoSizer = wx.BoxSizer(wx.VERTICAL)
        midSizer = wx.BoxSizer(wx.HORIZONTAL)
        collSizer = wx.BoxSizer(wx.VERTICAL)
        commentSizer = wx.BoxSizer(wx.VERTICAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        midSizer.Add(collSizer)
        midSizer.Add(commentSizer)
        panelSizer.Add(infoSizer)
        panelSizer.Add(midSizer)
        panelSizer.Add(btnSizer,flag=wx.ALIGN_RIGHT)
        
        # display info
        infoSizer.Add(wx.StaticText(win,-1,"This component will be uploaded with the following data:\nName: " +
                                    compName + "\nPackage: " + package + "\nPlease note that the name and package cannot be changed, neither in proceeding versions of this component.\nIn addition the following components will be uploaded as this component depends on them:\n"),flag=wx.ALL,border=10)

        # display list of collaborators
        self.collaborators = collaborators
        self.collaborators.sort()
        self.collBox = wx.ListBox(win,-1,choices=self.collaborators,size=(300,200), style=wx.LB_EXTENDED)
        collSizer.Add(wx.StaticText(win,-1,"Collaborators:"),flag=wx.ALL,border=5)
        collSizer.Add(self.collBox,flag=wx.ALL,border=5)
        
        # display controls to edit list
        self.newCollTb = wx.TextCtrl(win,-1)
        newCollBtn = wx.Button(win,wx.ID_ADD,'Add collaborator')
        removeBtn = wx.Button(win,wx.ID_REMOVE,'Remove')
        self.Bind(wx.EVT_BUTTON, self.EvtAddCollaborator, newCollBtn)
        self.Bind(wx.EVT_BUTTON, self.EvtRemoveCollaborator, removeBtn)
        addCollSizer = wx.BoxSizer(wx.HORIZONTAL)
        addCollSizer.Add(self.newCollTb,flag=wx.ALL,border=5)
        addCollSizer.Add(newCollBtn,flag=wx.ALL,border=5)
        addCollSizer.Add(removeBtn,flag=wx.ALL,border=5)
        collSizer.Add(addCollSizer)
        
        # comment
        commentSizer.Add(wx.StaticText(win,-1,'Enter a comment on this version:'),flag=wx.ALL,border=5)
        self.commentTxtCtrl = wx.TextCtrl(win,-1,style=wx.TE_MULTILINE,size=(250,150))
        commentSizer.Add(self.commentTxtCtrl,flag=wx.ALL,border=10)
        
        # buttons
        btn = wx.Button(win,wx.ID_OK,'Share')
        cancelBtn = wx.Button(win,wx.ID_CANCEL,'Cancel')
        btnSizer.Add(btn,flag=wx.ALL,border=10)
        btnSizer.Add(cancelBtn,flag=wx.ALL,border=10)
        
        win.SetSizer(panelSizer)
        win.SetAutoLayout(True)
        panelSizer.SetSizeHints(self)
        
    def EvtAddCollaborator(self, evt):
        collaborator = self.newCollTb.GetValue()
        if collaborator != '':
            self.collaborators.append(collaborator)
            self.collaborators.sort()
            self.collBox.Set(self.collaborators)
            self.newCollTb.SetValue('')
        
    def EvtRemoveCollaborator(self, evt):
        selectedColls = self.collBox.GetSelections()
        for s in selectedColls:
            self.collaborators.pop(s)
            self.collaborators.sort()
            self.collBox.Set(self.collaborators)
            self.collBox.SetSelection(wx.NOT_FOUND)
            
    def GetComment(self):
        return self.commentTxtCtrl.GetValue()
    
    def GetCollaborators(self):
        return self.collBox.GetItems()
        
    
class UserPassDialog(wx.Dialog):
    
    def __init__(self, frame, title='CSPShare access', info=''):
        wx.Dialog.__init__(self, frame, -1, title, style = wx.DEFAULT_FRAME_STYLE | wx.WANTS_CHARS )
        win = wx.Panel(self, -1)

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        ctrlSizer = wx.BoxSizer(wx.HORIZONTAL)
        leftSizer = wx.BoxSizer(wx.VERTICAL)
        rightSizer = wx.BoxSizer(wx.VERTICAL)
        
        if info != '':
            mainSizer.Add(wx.StaticText(win,-1,info),flag=wx.ALL,border=10)
        
        mainSizer.Add(ctrlSizer)
        ctrlSizer.Add(leftSizer)
        ctrlSizer.Add(rightSizer)
        
        leftSizer.Add(wx.StaticText(win,-1,'E-mail:'),flag=wx.ALL,border=10)
        leftSizer.Add(wx.StaticText(win,-1,'Password:'),flag=wx.ALL,border=10)
        
        global user, passwd
        self.userTextCtrl = wx.TextCtrl(win, -1, user, size=(300, -1), style=wx.TE_PROCESS_ENTER)
        rightSizer.Add(self.userTextCtrl, -1, wx.ALL, 5)
        self.passwordTextCtrl = wx.TextCtrl(win, -1, passwd, size=(300, -1), style=wx._controls.TE_PASSWORD|wx.TE_PROCESS_ENTER)
        rightSizer.Add(self.passwordTextCtrl, -1, wx.ALL, 5)
        
        self.Bind(wx.EVT_COMMAND_ENTER, self.EvtEnter, self.userTextCtrl)
        self.Bind(wx.EVT_COMMAND_ENTER, self.EvtEnter, self.passwordTextCtrl)

        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnOK = wx.Button(win, wx.ID_OK, "OK")
        btnCancel = wx.Button(win, wx.ID_CANCEL, "Cancel")
        buttonSizer.Add(btnOK, 0, wx.ALL, 5)
        buttonSizer.Add(btnCancel, 0, wx.ALL, 5)
        mainSizer.Add(buttonSizer,wx.ALIGN_RIGHT)

        win.SetSizer(mainSizer)
        win.SetAutoLayout(True)
        mainSizer.SetSizeHints(self)
        
    def EvtEnter(self, event):
        dlg = wx.MessageDialog(self, "Test!",'Test',wx.OK)
        dlg.ShowModal()
        dlg.Destroy()
        
    def GetEmail(self):
        return self.userTextCtrl.GetValue()
    
    def GetPassword(self):
        return self.passwordTextCtrl.GetValue()